package com.redwinter.logkit.support.appender;

import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.LevelFilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.spi.FilterReply;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.OptionHelper;
import com.redwinter.logkit.support.config.LogProperties;
import org.slf4j.LoggerFactory;

/**
 * @author goudadong
 * @description 日志Appender
 * @datetime 2021-12-16 16:53
 * @email 2360564660@qq.com
 **/
public class LogAppender {

    private final String filePath;
    private final LogProperties logProperties;
    public static String ASYNC_FILE = "ASYNC-FILE-";
    public static String FILE = "FILE-";

    public LogAppender(LogProperties logProperties) {
        this.logProperties = logProperties;
        String filePath = logProperties.getFilePath();
        this.filePath = filePath.endsWith("/") ? filePath : filePath + "/";
    }

    public RollingFileAppender<ILoggingEvent> rollingFileAppender(String name, String fileName, Level level) {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        // 设置appender的，在xml配置文件里面，是这种形式：
        // <appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
        // 设置级别过滤器
        LevelFilter levelFilter = getLevelFilter(level);
        levelFilter.start();
        appender.addFilter(levelFilter);
        
        // 设置上下文，每个logger都关联到logger上下文，默认上下文名称为default。
        // 但可以使用<contextName>设置成其他名字，用于区分不同应用程序的记录。一旦设置，不能修改。
        appender.setContext(context);
        // appender的name属性
        appender.setName(FILE + name);
        // 设置文件名
        fileName = filePath + (fileName == null ? level.levelStr : fileName);
        appender.setFile(OptionHelper.substVars(fileName + ".log", context));
        appender.setAppend(true);
        appender.setPrudent(false);

        SizeAndTimeBasedRollingPolicy<ILoggingEvent> policy = sizeAndTimeBasedRollingPolicy(fileName, context, appender);
        PatternLayoutEncoder encoder = patternLayoutEncoder(context);

        //加入下面两个节点
        appender.setRollingPolicy(policy);
        appender.setEncoder(encoder);
        appender.start();
        return appender;

    }

    private PatternLayoutEncoder patternLayoutEncoder(LoggerContext context) {
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        //设置上下文，每个logger都关联到logger上下文，默认上下文名称为default。
        // 但可以使用<contextName>设置成其他名字，用于区分不同应用程序的记录。一旦设置，不能修改。
        encoder.setContext(context);
        //设置格式
        encoder.setPattern("%m%n");
        encoder.start();
        return encoder;
    }

    private SizeAndTimeBasedRollingPolicy<ILoggingEvent> sizeAndTimeBasedRollingPolicy(String fileName, LoggerContext context, RollingFileAppender<ILoggingEvent> appender) {
        //设置文件创建时间及大小的类
        SizeAndTimeBasedRollingPolicy<ILoggingEvent> policy = new SizeAndTimeBasedRollingPolicy<>();
        //文件名格式
        String fp = OptionHelper.substVars(fileName + ".log.%d{yyyy-MM-dd}.%i.zip", context);
        //设置上下文，每个logger都关联到logger上下文，默认上下文名称为default。
        // 但可以使用<contextName>设置成其他名字，用于区分不同应用程序的记录。一旦设置，不能修改。
        policy.setContext(context);
        //最大日志文件大小
        policy.setMaxFileSize(FileSize.valueOf(logProperties.getMaxFileSize()));
        //设置文件名模式
        policy.setFileNamePattern(fp);
        //设置最大历史记录
        policy.setMaxHistory(logProperties.getMaxHistory());
        //总大小限制
        policy.setTotalSizeCap(FileSize.valueOf(logProperties.getTotalSizeCap()));
        //设置父节点是appender
        policy.setParent(appender);
        policy.start();
        return policy;
    }

    public AsyncAppender asyncAppender(String name,String fileName,Level level){
        AsyncAppender asyncAppender = new AsyncAppender();
        RollingFileAppender<ILoggingEvent> rollingFileAppender = rollingFileAppender(name, fileName, level);
        asyncAppender.setContext(rollingFileAppender.getContext());
        asyncAppender.addAppender(rollingFileAppender);
        asyncAppender.setName(ASYNC_FILE+name);
        asyncAppender.setQueueSize(512);
        // 丢弃阈值
        asyncAppender.setDiscardingThreshold(0);
        asyncAppender.start();
        return asyncAppender;
    }

    private LevelFilter getLevelFilter(Level level) {
        LevelFilter levelFilter = new LevelFilter();
        levelFilter.setLevel(level);
        levelFilter.setOnMatch(FilterReply.ACCEPT);
        levelFilter.setOnMismatch(FilterReply.DENY);
        return levelFilter;
    }
}
